#pragma once

#include <exception>
#include <string>

#ifdef __GNUC__
#define FUNCTION_NAME __PRETTY_FUNCTION__
#elif defined(_MSC_VER)
#define FUNCTION_NAME __FUNCSIG__
#else
#define FUNCTION_NAME __func__
#endif


#define Cm_Release_Exception_Start try {
#define Cm_Release_Exception_Empty_Catch(EXCEPTION_TYPE, expr) \
    }                                                          \
    catch (const EXCEPTION_TYPE &e) {                          \
        { expr; };
#define Cm_Release_Exception_Catch(EXCEPTION_TYPE, expr) Cm_Release_Exception_Empty_Catch(EXCEPTION_TYPE, { \
    std::cerr << "Caught exception: in function " << e.Function()                                       \
                << " at " << e.File()                                                                         \
                << ":" << e.Line()                                                                      \
                << " with message: " << e.what() << std::endl;                                                \
    { expr; };                                                                                              \
})
#define Cm_Release_Exception_End(expr)                                            \
    Cm_Release_Exception_Catch(CustomException, { expr; })                         \
    }                                                                             \
    catch (...) {                                                                 \
        std::cerr << "Caught unknown exception: in function " << FUNCTION_NAME \
                    << " at " << __FILE__                                           \
                    << " line " << __LINE__ << std::endl;                           \
        { expr; };                                                                \
    }


#define ThrowCustomExceptionInstance(msg) throw CustomException(msg, __FILE__, __LINE__, FUNCTION_NAME)
#define ThrowCustomException() throw CustomException(__FILE__, __LINE__, FUNCTION_NAME)


enum ExceptionType {
    Unknown,
    FileNotFound,
    FileNotOpen,
    FileNotClosed,
    FileNotRead,
    FileNotWritten,
    FileNotSeeked,
    FileNotTruncated,
    FileNotFlushed,
    FileNotRenamed,
    FileNotDeleted,
    FileNotCreated,
    FileNotMoved,
    FileNotCopied,
    FileNotMerged,
};


class CustomException : public std::exception {
public:
    CustomException(const std::string& message) : message_(message) {}
    CustomException(const std::string& file, int line, const std::string func_name) : file_(file), line_(line), function_(func_name) {}
    CustomException(const std::string& message, const std::string& file, int line, const std::string func_name) : message_(message), file_(file), line_(line), function_(func_name) {}

    const char* what() const noexcept override {
        return message_.c_str();
    }

    std::string Message() const {
        return message_;
    }

    std::string File() const {
        return file_;
    }

    std::string Function() const {
        return function_;
    }

    int Line() const {
        return line_;
    }

private:
    std::string message_;
    std::string file_;
    std::string function_;
    int line_;
};